package com.wjp.banquet.common.config;

import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.google.common.base.Optional;
import com.google.common.base.Predicates;
import com.wjp.banquet.common.annotation.SwaggerDisplayEnum;
import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ModelPropertyBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.Annotations;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.schema.ApiModelProperties;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Author 王君平
 * @Date 2023/8/28 15:29
 * @Description
 */
@Slf4j
@Configuration
@EnableSwagger2
public class SwaggerConfig implements ModelPropertyBuilderPlugin {


    @Bean
    public Docket CreateRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .pathMapping("/")
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.wjp.banquet.controller"))
                .paths(Predicates.not(PathSelectors.regex("/error.*")))
                .paths(PathSelectors.regex("/.*"))
                .build();
    }

    private ApiInfo apiInfo () {
        return new ApiInfoBuilder()
                .title("酒宴")
                //.description("暂无描述")
                //.termsOfServiceUrl("https://www.XXX.XXX")
                .contact(new Contact("wjp", "", "569139849@qq.com"))
                .version("V1.0")
                .build();
    }

    /**
     * 为枚举字段设置注释
     * 因为要将字段枚举值放到swagger,这里才选择实现ModelPropertyBuilderPlugin接口的方法  https://blog.csdn.net/weixin_50276625/article/details/115858744
     * @param
     */
    @Override
    public void apply(ModelPropertyContext context) {

        AnnotatedField annotatedField = context.getBeanPropertyDefinition().get().getField();
        //获取当前字段的类型
        final Class fieldType = annotatedField.getRawType();
        SwaggerDisplayEnum swaggerDisplayEnum = annotatedField.getAnnotation(SwaggerDisplayEnum.class);
        if(swaggerDisplayEnum == null || swaggerDisplayEnum.value() == null){
            return;
        }
        //为枚举字段设置注释
        Optional<ApiModelProperty> annotation = Optional.absent();

        // 找到 @ApiModelProperty 注解修饰的枚举类
        if (context.getAnnotatedElement().isPresent()) {
            annotation = annotation
                    .or(ApiModelProperties.findApiModePropertyAnnotation(context.getAnnotatedElement().get()));
        }
        if (context.getBeanPropertyDefinition().isPresent()) {
            annotation = annotation.or(Annotations.findPropertyAnnotation(
                    context.getBeanPropertyDefinition().get(),
                    ApiModelProperty.class));
        }

        //没有@ApiModelProperty 或者 notes 属性没有值，直接返回
        if (!annotation.isPresent() ) {
            return;
        }


        Class rawPrimaryType = swaggerDisplayEnum.value();

        // 拿到枚举的所有的值
        Object[] subItemRecords = rawPrimaryType.getEnumConstants();
        if (null == subItemRecords) {
            return;
        }

        final List<String> displayValues =
                Arrays.stream(subItemRecords)
                        .filter(Objects::nonNull)
                        // 调用枚举类的 toString 方法
                        .map(Object::toString)
                        .filter(Objects::nonNull)
                        .collect(Collectors.toList());

        String joinText = " (" + String.join("; ", displayValues) + ")";
        try {
            // 拿到字段上原先的描述
            Field mField = ModelPropertyBuilder.class.getDeclaredField("description");
            mField.setAccessible(true);
            // context 中的 builder 对象保存了字段的信息
            joinText = mField.get(context.getBuilder()) + joinText;
        } catch (Exception e) {
            log.error(e.getMessage());
        }

        // 设置新的字段说明并且设置字段类型
        final ResolvedType resolvedType = context.getResolver().resolve(fieldType);
        context.getBuilder().description(joinText).type(resolvedType);
    }

    @Override
    public boolean supports(DocumentationType documentationType) {
        return true;
    }


}


